home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / COPYSUPPORT.PY < prev    next >
Encoding:
Python Source  |  2000-08-21  |  20.0 KB  |  549 lines

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64. __doc__="""Copy interface"""
  65. __version__='$Revision: 1.55.8.2 $'[11:-2]
  66.  
  67. import sys, string, Globals, Moniker, tempfile, ExtensionClass
  68. from marshal import loads, dumps
  69. from urllib import quote, unquote
  70. from zlib import compress, decompress
  71. from App.Dialogs import MessageDialog
  72. from AccessControl import getSecurityManager
  73.  
  74.  
  75. CopyError='Copy Error'
  76.  
  77. _marker=[]
  78. class CopyContainer(ExtensionClass.Base):
  79.     """Interface for containerish objects which allow cut/copy/paste"""
  80.  
  81.     __ac_permissions__=(
  82.         ('View management screens',
  83.          ('manage_cutObjects', 'manage_copyObjects', 'manage_pasteObjects',
  84.           'manage_renameForm', 'manage_renameObject', 'manage_renameObjects',)),
  85.         )
  86.  
  87.  
  88.     # The following three methods should be overridden to store sub-objects
  89.     # as non-attributes.
  90.     def _setOb(self, id, object): setattr(self, id, object)
  91.     def _delOb(self, id): delattr(self, id)
  92.     def _getOb(self, id, default=_marker):
  93.         if hasattr(self, 'aq_base'): self=self.aq_base
  94.         if default is _marker: return getattr(self, id)
  95.         try: return getattr(self, id)
  96.         except: return default
  97.  
  98.  
  99.     def manage_CopyContainerFirstItem(self, REQUEST):
  100.         return self._getOb(REQUEST['ids'][0])
  101.  
  102.     def manage_CopyContainerAllItems(self, REQUEST):
  103.         return map(lambda i, s=self: s._getOb(i), tuple(REQUEST['ids']))
  104.  
  105.     def manage_cutObjects(self, ids, REQUEST=None):
  106.         """Put a reference to the objects named in ids in the clip board"""
  107.         if type(ids) is type(''):
  108.             ids=[ids]
  109.         oblist=[]
  110.         for id in ids:
  111.             ob=self._getOb(id)
  112.             if not ob.cb_isMoveable():
  113.                 raise CopyError, eNotSupported % id
  114.             m=Moniker.Moniker(ob)
  115.             oblist.append(m.dump())
  116.         cp=(1, oblist)
  117.         cp=_cb_encode(cp)
  118.         if REQUEST is not None:
  119.             resp=REQUEST['RESPONSE']
  120.             resp.setCookie('__cp', cp, path='%s' % REQUEST['SCRIPT_NAME'])
  121.             return self.manage_main(self, REQUEST, cb_dataValid=1)
  122.         return cp
  123.     
  124.     def manage_copyObjects(self, ids, REQUEST=None, RESPONSE=None):
  125.         """Put a reference to the objects named in ids in the clip board"""
  126.         if type(ids) is type(''):
  127.             ids=[ids]
  128.         oblist=[]
  129.         for id in ids:
  130.             ob=self._getOb(id)
  131.             if not ob.cb_isCopyable():
  132.                 raise CopyError, eNotSupported % id
  133.             m=Moniker.Moniker(ob)
  134.             oblist.append(m.dump())
  135.         cp=(0, oblist)
  136.         cp=_cb_encode(cp)
  137.         if REQUEST is not None:
  138.             resp=REQUEST['RESPONSE']
  139.             resp.setCookie('__cp', cp, path='%s' % REQUEST['SCRIPT_NAME'])
  140.             return self.manage_main(self, REQUEST, cb_dataValid=1)
  141.         return cp
  142.  
  143.     def _get_id(self, id):
  144.         # Allow containers to override the generation of
  145.         # object copy id by attempting to call its _get_id
  146.         # method, if it exists.
  147.         n=0
  148.         if (len(id) > 8) and (id[8:]=='copy_of_'):
  149.             n=1
  150.         orig_id=id
  151.         while 1:
  152.             if self._getOb(id, None) is None:
  153.                 return id
  154.             id='copy%s_of_%s' % (n and n+1 or '', orig_id)
  155.             n=n+1
  156.  
  157.     def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
  158.         """Paste previously copied objects into the current object.
  159.            If calling manage_pasteObjects from python code, pass
  160.            the result of a previous call to manage_cutObjects or
  161.            manage_copyObjects as the first argument."""
  162.         cp=None
  163.         if cb_copy_data is not None:
  164.             cp=cb_copy_data
  165.         else:
  166.             if REQUEST and REQUEST.has_key('__cp'):
  167.                 cp=REQUEST['__cp']
  168.         if cp is None:
  169.             raise CopyError, eNoData
  170.         
  171.         try:    cp=_cb_decode(cp)
  172.         except: raise CopyError, eInvalid
  173.  
  174.         oblist=[]
  175.         op=cp[0]
  176.         app = self.getPhysicalRoot()
  177.  
  178.         for mdata in cp[1]:
  179.             m = Moniker.loadMoniker(mdata)
  180.             try: ob = m.bind(app)
  181.             except: raise CopyError, eNotFound
  182.             self._verifyObjectPaste(ob)
  183.             oblist.append(ob)
  184.  
  185.         if op==0:
  186.             # Copy operation
  187.             for ob in oblist:
  188.                 if not ob.cb_isCopyable():
  189.                     raise CopyError, eNotSupported % absattr(ob.id)
  190.                 try:    ob._notifyOfCopyTo(self, op=0)
  191.                 except: raise CopyError, MessageDialog(
  192.                     title='Copy Error',
  193.                     message=sys.exc_info()[1],
  194.                     action ='manage_main')
  195.                 ob=ob._getCopy(self)
  196.                 ob.manage_afterClone(ob)
  197.                 id=self._get_id(absattr(ob.id))
  198.                 ob._setId(id)
  199.                 self._setObject(id, ob)
  200.  
  201.             if REQUEST is not None:
  202.                 return self.manage_main(self, REQUEST, update_menu=1,
  203.                                         cb_dataValid=1)
  204.  
  205.         if op==1:
  206.             # Move operation
  207.             for ob in oblist:
  208.                 id=absattr(ob.id)
  209.                 if not ob.cb_isMoveable():
  210.                     raise CopyError, eNotSupported % id
  211.                 try:    ob._notifyOfCopyTo(self, op=1)
  212.                 except: raise CopyError, MessageDialog(
  213.                     title='Move Error',
  214.                     message=sys.exc_info()[1],
  215.                     action ='manage_main')
  216.                 if not sanity_check(self, ob):
  217.                     raise CopyError, 'This object cannot be pasted into itself'
  218.  
  219.                 # try to make ownership explicit so that it gets carried
  220.                 # along to the new location if needed.
  221.                 ob.manage_changeOwnershipType(explicit=1)
  222.                 
  223.                 ob.aq_parent._delObject(id)
  224.                 if hasattr(ob, 'aq_base'):
  225.                     ob=ob.aq_base
  226.                 id=self._get_id(id)
  227.                 ob._setId(id)
  228.  
  229.                 self._setObject(id, ob, set_owner=0)
  230.  
  231.                 # try to make ownership implicit if possible
  232.                 ob=self._getOb(id)
  233.                 ob.manage_changeOwnershipType(explicit=0)
  234.  
  235.             if REQUEST is not None:
  236.                 REQUEST['RESPONSE'].setCookie('cp_', 'deleted',
  237.                                     path='%s' % REQUEST['SCRIPT_NAME'],
  238.                                     expires='Wed, 31-Dec-97 23:59:59 GMT')
  239.                 return self.manage_main(self, REQUEST, update_menu=1,
  240.                                         cb_dataValid=0)
  241.         return ''
  242.  
  243.  
  244.     manage_renameForm=Globals.HTMLFile('renameForm', globals())
  245.  
  246.     def manage_renameObjects(self, ids, new_ids, REQUEST=None):
  247.         """Rename several sub-objects"""
  248.         if len(ids) != len(new_ids):
  249.             raise 'BadRequst','Please rename each listed object.'
  250.         for i in range(len(ids)):
  251.             if ids[i] != new_ids[i]:
  252.                 self.manage_renameObject(ids[i], new_ids[i], REQUEST)
  253.         if REQUEST is not None:
  254.             return self.manage_main(self, REQUEST, update_menu=1)
  255.         return None
  256.  
  257.     def manage_renameObject(self, id, new_id, REQUEST=None):
  258.         """Rename a particular sub-object"""
  259.         try: self._checkId(new_id)
  260.         except: raise CopyError, MessageDialog(
  261.                       title='Invalid Id',
  262.                       message=sys.exc_info()[1],
  263.                       action ='manage_main')
  264.         ob=self._getOb(id)
  265.         if not ob.cb_isMoveable():
  266.             raise CopyError, eNotSupported % id            
  267.         self._verifyObjectPaste(ob)
  268.         try:    ob._notifyOfCopyTo(self, op=1)
  269.         except: raise CopyError, MessageDialog(
  270.                       title='Rename Error',
  271.                       message=sys.exc_info()[1],
  272.                       action ='manage_main')
  273.         self._delObject(id)
  274.         if hasattr(ob, 'aq_base'):
  275.             ob=ob.aq_base
  276.         ob._setId(new_id)
  277.         
  278.         # Note - because a rename always keeps the same context, we
  279.         # can just leave the ownership info unchanged.
  280.         self._setObject(new_id, ob, set_owner=0)
  281.  
  282.         if REQUEST is not None:
  283.             return self.manage_main(self, REQUEST, update_menu=1)
  284.         return None
  285.  
  286.     # Why did we give this a manage_ prefix if its really
  287.     # supposed to be public since it does its own auth ?
  288.     #
  289.     # Because it's still a "management" function.
  290.     manage_clone__roles__=None
  291.     def manage_clone(self, ob, id, REQUEST=None):
  292.         # Clone an object, creating a new object with the given id.
  293.         if not ob.cb_isCopyable():
  294.             raise CopyError, eNotSupported % absattr(ob.id)            
  295.         try: self._checkId(id)
  296.         except: raise CopyError, MessageDialog(
  297.                       title='Invalid Id',
  298.                       message=sys.exc_info()[1],
  299.                       action ='manage_main')
  300.         self._verifyObjectPaste(ob)
  301.         try:    ob._notifyOfCopyTo(self, op=0)
  302.         except: raise CopyError, MessageDialog(
  303.                       title='Clone Error',
  304.                       message=sys.exc_info()[1],
  305.                       action ='manage_main')
  306.         ob=ob._getCopy(self)
  307.         ob._setId(id)
  308.         self._setObject(id, ob)
  309.         ob=ob.__of__(self)
  310.         #ob._postCopy(self, op=0)
  311.         return ob
  312.  
  313.     def cb_dataValid(self):
  314.         # Return true if clipboard data seems valid.
  315.         try:    cp=_cb_decode(self.REQUEST['__cp'])
  316.         except: return 0
  317.         return 1
  318.  
  319.     def cb_dataItems(self):
  320.         # List of objects in the clip board
  321.         try:    cp=_cb_decode(self.REQUEST['__cp'])
  322.         except: return []
  323.         oblist=[]
  324.  
  325.         app = self.getPhysicalRoot()
  326.         for mdata in cp[1]:
  327.             m = Moniker.loadMoniker(mdata)
  328.             oblist.append(m.bind(app))
  329.         return oblist
  330.  
  331.     validClipData=cb_dataValid
  332.  
  333.     def _verifyObjectPaste(self, object, validate_src=1):
  334.         # Verify whether the current user is allowed to paste the
  335.         # passed object into self. This is determined by checking
  336.         # to see if the user could create a new object of the same
  337.         # meta_type of the object passed in and checking that the
  338.         # user actually is allowed to access the passed in object
  339.         # in its existing context.
  340.         #
  341.         # Passing a false value for the validate_src argument will skip
  342.         # checking the passed in object in its existing context. This is
  343.         # mainly useful for situations where the passed in object has no 
  344.         # existing context, such as checking an object during an import
  345.         # (the object will not yet have been connected to the acquisition
  346.         # heirarchy).
  347.         if not hasattr(object, 'meta_type'):
  348.             raise CopyError, MessageDialog(
  349.                   title='Not Supported',
  350.                   message='The object <EM>%s</EM> does not support this ' \
  351.                           'operation' % absattr(object.id),
  352.                   action='manage_main')
  353.         mt=object.meta_type
  354.         if not hasattr(self, 'all_meta_types'):
  355.             raise CopyError, MessageDialog(
  356.                   title='Not Supported',
  357.                   message='Cannot paste into this object.',
  358.                   action='manage_main')
  359.  
  360.         method_name=None
  361.         meta_types=absattr(self.all_meta_types)
  362.         for d in meta_types:
  363.             if d['name']==mt:
  364.                 method_name=d['action']
  365.                 break
  366.  
  367.         if method_name is not None:
  368.             meth=self.unrestrictedTraverse(method_name)
  369.             try:    parent=object.aq_inner.aq_parent
  370.             except: parent=None
  371.             if getSecurityManager().validate(None, parent, None, meth):
  372.                 # Ensure the user is allowed to access the object on the
  373.                 # clipboard.
  374.                 if not validate_src:
  375.                     return
  376.                 try:    parent=object.aq_inner.aq_parent
  377.                 except: parent=None
  378.                 if getSecurityManager().validate(None, parent, None, object):
  379.                     return
  380.  
  381.         raise CopyError, MessageDialog(
  382.               title='Not Supported',
  383.               message='The object <EM>%s</EM> does not support this ' \
  384.                       'operation' % absattr(object.id),
  385.               action='manage_main')
  386.  
  387. Globals.default__class_init__(CopyContainer)
  388.  
  389.  
  390.  
  391. class CopySource:
  392.     """Interface for objects which allow themselves to be copied."""
  393.     
  394.     def _canCopy(self, op=0):
  395.         """Called to make sure this object is copyable. The op var
  396.         is 0 for a copy, 1 for a move."""
  397.         return 1
  398.  
  399.     def _notifyOfCopyTo(self, container, op=0):
  400.         """Overide this to be pickly about where you go! If you dont
  401.         want to go there, raise an exception. The op variable is
  402.         0 for a copy, 1 for a move."""
  403.         pass
  404.  
  405.     def _getCopy(self, container):
  406.         # Ask an object for a new copy of itself.
  407.         f=tempfile.TemporaryFile()
  408.         self._p_jar.exportFile(self._p_oid,f)
  409.         f.seek(0)
  410.         ob=container._p_jar.importFile(f)
  411.         f.close()
  412.         return ob
  413.  
  414.     def _postCopy(self, container, op=0):
  415.         # Called after the copy is finished to accomodate special cases.
  416.         # The op var is 0 for a copy, 1 for a move.
  417.         pass
  418.     
  419.     def _setId(self, id):
  420.         # Called to set the new id of a copied object.
  421.         self.id=id
  422.  
  423.     def cb_isCopyable(self):
  424.         # Is object copyable? Returns 0 or 1
  425.         if not (hasattr(self, '_canCopy') and self._canCopy(0)):
  426.             return 0
  427.         if hasattr(self, '_p_jar') and self._p_jar is None:
  428.             return 0
  429.         return 1
  430.  
  431.     def cb_isMoveable(self):
  432.         # Is object moveable? Returns 0 or 1
  433.         if not (hasattr(self, '_canCopy') and self._canCopy(1)):
  434.             return 0
  435.         if hasattr(self, '_p_jar') and self._p_jar is None:
  436.             return 0
  437.         try:    n=self.aq_parent._reserved_names
  438.         except: n=()
  439.         if absattr(self.id) in n:
  440.             return 0
  441.         return 1
  442.  
  443.  
  444.  
  445. def sanity_check(c, ob):
  446.     # This is called on cut/paste operations to make sure that
  447.     # an object is not cut and pasted into itself or one of its
  448.     # subobjects, which is an undefined situation.
  449.     ob=getattr(ob, 'aq_base', ob)
  450.     while 1:
  451.         if getattr(c, 'aq_base', c) is ob:
  452.             return 0
  453.         if not hasattr(c, 'aq_parent'):
  454.             return 1
  455.         c=c.aq_parent
  456.  
  457. def absattr(attr):
  458.     if callable(attr): return attr()
  459.     return attr
  460.  
  461. def _cb_encode(d):
  462.     return quote(compress(dumps(d), 9))
  463.  
  464. def _cb_decode(s):
  465.     return loads(decompress(unquote(s)))
  466.  
  467.  
  468.  
  469.  
  470. fMessageDialog=Globals.HTML("""
  471. <HTML>
  472. <HEAD>
  473. <TITLE><dtml-var title></TITLE>
  474. </HEAD>
  475. <BODY BGCOLOR="#FFFFFF">
  476. <FORM ACTION="<dtml-var action>" METHOD="GET" <dtml-if
  477.  target>TARGET="<dtml-var target>"</dtml-if>>
  478. <TABLE BORDER="0" WIDTH="100%%" CELLPADDING="10">
  479. <TR>
  480.   <TD VALIGN="TOP">
  481.   <BR>
  482.   <CENTER><B><FONT SIZE="+6" COLOR="#77003B">!</FONT></B></CENTER>
  483.   </TD>
  484.   <TD VALIGN="TOP">
  485.   <BR><BR>
  486.   <CENTER>
  487.   <dtml-var message>
  488.   </CENTER>
  489.   </TD>
  490. </TR>
  491. <TR>
  492.   <TD VALIGN="TOP">
  493.   </TD>
  494.   <TD VALIGN="TOP">
  495.   <CENTER>
  496.   <INPUT TYPE="SUBMIT" VALUE="   Ok   ">
  497.   </CENTER>
  498.   </TD>
  499. </TR>
  500. </TABLE>
  501. </FORM>
  502. </BODY></HTML>""", target='', action='manage_main', title='Changed')
  503.  
  504.  
  505. eNoData=MessageDialog(
  506.         title='No Data',
  507.         message='No clipboard data found.',
  508.         action ='manage_main',)
  509.  
  510. eInvalid=MessageDialog(
  511.          title='Clipboard Error',
  512.          message='The data in the clipboard could not be read, possibly due ' \
  513.          'to cookie data being truncated by your web browser. Try copying ' \
  514.          'fewer objects.',
  515.          action ='manage_main',)
  516.  
  517. eNotFound=MessageDialog(
  518.           title='Item Not Found',
  519.           message='One or more items referred to in the clipboard data was ' \
  520.           'not found. The item may have been moved or deleted after you ' \
  521.           'copied it.',
  522.           action ='manage_main',)
  523.  
  524. eNotSupported=fMessageDialog(
  525.               title='Not Supported',
  526.               message='The item <EM>%s</EM> does not support this operation.',
  527.               action ='manage_main',)
  528.